#include "CInputManager.h"
#include "Utility.h"

#define KEYDOWN(name, key) (name[key] & 0x80  ? true : false)
#define MOUSEDOWN(name, key) (name.rgbButtons[key] & 0x80 ? true : false)

CInputManager::CInputManager()
{

}


CInputManager::~CInputManager()
{

}


CInputManager* CInputManager::GetInstance()
{
	static CInputManager manager;
	return &manager;
}

bool CInputManager::InitKeyboard(HWND hWnd, HINSTANCE hInstance, bool bIsExclusive)
{
	m_hWnd = hWnd;

	if(!m_pDIn)
	{
		if(DI_OK != DirectInput8Create(hInstance, DIRECTINPUT_VERSION, IID_IDirectInput8, (void**)&m_pDIn, 0))
			return false;
	}

	if(!m_pKeyboard)
	{
		if(((HRESULT)0L) != m_pDIn->CreateDevice(GUID_SysKeyboard, &m_pKeyboard, NULL))
			return false;
	}

	m_pKeyboard->SetDataFormat(&c_dfDIKeyboard);
	m_pKeyboard->SetCooperativeLevel(hWnd, DISCL_FOREGROUND | (bIsExclusive ? DISCL_EXCLUSIVE : DISCL_NONEXCLUSIVE));

	return true;
}


bool CInputManager::InitMouse(HWND hWnd, HINSTANCE hInstance, bool bIsExclusive)
{
	m_hWnd = hWnd;
	if(!m_pDIn)
		if(DI_OK != DirectInput8Create(hInstance, DIRECTINPUT_VERSION, IID_IDirectInput8, (void**)&m_pDIn, 0))
			return false;

	if(!m_pMouse)
		if(((HRESULT)0L) != m_pDIn->CreateDevice(GUID_SysMouse, &m_pMouse, NULL))
			return false;

	m_pMouse->SetDataFormat(&c_dfDIMouse);
	m_pMouse->SetCooperativeLevel(hWnd,  bIsExclusive ? DISCL_EXCLUSIVE : DISCL_NONEXCLUSIVE);
	return true;
}


bool CInputManager::InitDirectinput(HWND hWnd, HINSTANCE hInstance, bool bMouseExclusive, bool bKeyboardExclusive)
{
	if(!InitKeyboard(hWnd, hInstance, bKeyboardExclusive))
	{
		LOG("ERROR - KEYBOARD DID NOT GET SETUP!!!!!");
	}

	if(!InitMouse(hWnd, hInstance, bMouseExclusive))
	{
		LOG("ERROR - MOUSE DID NOT GET SETUP!!!!!");
	}

	return true;
}


void CInputManager::ShutDownDirectInput()
{
	if(m_pMouse)
		m_pMouse->Release();
	if(m_pKeyboard)
		m_pKeyboard->Release();
	if(m_pDIn)
		m_pDIn->Release();
}


void CInputManager::ReadDevices()
{

}


void CInputManager::ClearInput()
{
	ZeroMemory(m_KeyState, 256);
	ZeroMemory(m_OldKeyState, 256);
	ZeroMemory(m_Keys, 256);
}


void CInputManager::AquireAll()
{
	m_pKeyboard->Acquire();
	m_pMouse->Acquire();
}


void CInputManager::UnacquireAll()
{
	m_pKeyboard->Unacquire();
	m_pMouse->Unacquire();
}

bool CInputManager::KeyDown(unsigned char ucDIKey)
{
	return m_KeyState[ucDIKey];
}


bool CInputManager::KeyPressed(unsigned char ucDIKey)
{
	return m_KeyState[ucDIKey];
}


bool CInputManager::KeyPressedEx(unsigned char ucDIKey)
{
	return (!m_OldKeyState[ucDIKey] && m_KeyState[ucDIKey]);
}


bool CInputManager::KeyUp(unsigned char ucDIKey)
{
	return !m_KeyState[ucDIKey];
}


bool CInputManager::KeyReleased(unsigned char ucDIKey)
{
	return !m_KeyState[ucDIKey];
}


bool CInputManager::KeyReleasedEx(unsigned char ucDIKey)
{
	return (m_OldKeyState[ucDIKey] && !m_KeyState[ucDIKey]);
}


bool CInputManager::CheckKeys(unsigned char& c)
{
	for (unsigned int i = 0; i < 256; ++i)
	{
		if(!m_KeyState[i] && m_OldKeyState[i])
		{
			c = i;
			return true;
		}
	}

	return false;
}


char CInputManager::CheckBufferedKeysEx()
{
	HKL hklKeyboardLayout = GetKeyboardLayout(0); 
	UINT virtualKey = 0;
	for(int i  = 0; i < 256; ++i)
	{
		if(m_KeyState[i] && !m_OldKeyState[i])
		{
			virtualKey = MapVirtualKeyEx(i, 3, hklKeyboardLayout);
			break;
		}
	}

	// Return the Ascii Value.
	return ((m_KeyState[DIK_LSHIFT] || m_KeyState[DIK_RSHIFT]) ? (char)virtualKey : tolower((char)virtualKey));
}


//unsigned char CInputManager::GetDIKCode()
//{
//
//}


//unsigned char CInputManager::GetBufferedDIKCodeEx()
//{
//
//}


bool CInputManager::MouseButtonDown(unsigned char ucButton)
{
	ucButton &= 0x7;
	return MOUSEDOWN(m_MouseState, ucButton);
}


bool CInputManager::MouseButtonPressed(unsigned char ucButton)
{
	ucButton &= 0x7;
	return MOUSEDOWN(m_MouseState, ucButton);
}


bool CInputManager::MouseButtonPressedEx(unsigned char ucButton)
{
	ucButton &= 0x7;
	return (!MOUSEDOWN(m_OldMouseState, ucButton) && MOUSEDOWN(m_MouseState, ucButton));
}


bool CInputManager::MouseButtonUp(unsigned char ucButton)
{
	ucButton &= 0x7;
	return !MOUSEDOWN(m_MouseState, ucButton);
}


bool CInputManager::MouseButtonReleased(unsigned char ucButton)
{
	ucButton &= 0x7;
	return !MOUSEDOWN(m_MouseState, ucButton);
}


bool CInputManager::MouseButtonReleasedEx(unsigned char ucButton)
{
	ucButton &= 0x7;
	return (MOUSEDOWN(m_OldMouseState, ucButton) && !MOUSEDOWN(m_MouseState, ucButton));
}


//int	CInputManager::MouseCheckButtons()
//{
//
//}
//
//
//int	CInputManager::MouseCheckBufferedButtonsEx()
//{
//
//}


long CInputManager::MouseGetPosX()
{
	POINT cursor;
	GetCursorPos(&cursor);
	ScreenToClient(m_hWnd, &cursor);
	return cursor.x;
}


long CInputManager::MouseGetPosY()
{
	POINT cursor;
	GetCursorPos(&cursor);
	ScreenToClient(m_hWnd, &cursor);
	return cursor.y;
}


long CInputManager::MouseWheelMovement()
{
	return m_MouseState.lZ;
}


//int	CInputManager::MouseGetNumButtons()
//{
//
//}


char* CInputManager::GetKeyName(unsigned char scanCode)
{
	switch(scanCode)
	{
	case DIK_ESCAPE:
		return "Escape";
		break;
	case DIK_1 :
		return "1";
		break;          
	case DIK_2 :
		return "2";
		break;          
	case DIK_3  :
		return "3";
		break;         
	case DIK_4  :
		return "4";
		break;         
	case DIK_5  :
		return "5";
		break;         
	case DIK_6  :
		return "6";
		break;         
	case DIK_7  :
		return "7";
		break;         
	case DIK_8  :
		return "8";
		break;         
	case DIK_9  :
		return "9";
		break;         
	case DIK_0  :
		return "0";
		break;         
	case DIK_MINUS :
		return "Minus";
		break;      
	case DIK_EQUALS  :
		return "Equals";
		break;    
	case DIK_BACK  :
		return "Back";
		break;      
	case DIK_TAB  :
		return "Tab";
		break;       
	case DIK_Q  :
		return "Q";
		break;         
	case DIK_W  :
		return "W";
		break;         
	case DIK_E     :
		return "E";
		break;      
	case DIK_R     :
		return "R";
		break;      
	case DIK_T     :
		return "T";
		break;      
	case DIK_Y     :
		return "Y";
		break;      
	case DIK_U     :
		return "U";
		break;      
	case DIK_I     :
		return "I";
		break;      
	case DIK_O     :
		return "O";
		break;      
	case DIK_P       :
		return "P";
		break;    
	case DIK_LBRACKET    :
		return "Left Bracket";
		break;
	case DIK_RBRACKET    :
		return "Right Bracket";
		break;
	case DIK_RETURN    :
		return "Return";
		break;  
	case DIK_LCONTROL  :
		return "Left Control";
		break;  
	case DIK_A      :
		return "A";
		break;     
	case DIK_S     :
		return "S";
		break;      
	case DIK_D     :
		return "D";
		break;      
	case DIK_F     :
		return "F";
		break;      
	case DIK_G     :
		return "G";
		break;      
	case DIK_H     :
		return "H";
		break;      
	case DIK_J    :
		return "J";
		break;       
	case DIK_K    :
		return "K";
		break;       
	case DIK_L     :
		return "L";
		break;      
	case DIK_SEMICOLON  :
		return "Semicolon";
		break; 
	case DIK_APOSTROPHE:
		return "Apostrophe";
		break;
	case DIK_GRAVE     :
		return "Grave";
		break;  
	case DIK_LSHIFT    :
		return "Left Shift";
		break;  
	case DIK_BACKSLASH   :
		return "Backslash";
		break;
	case DIK_Z     :
		return "Z";
		break;      
	case DIK_X    :
		return "X";
		break;       
	case DIK_C    :
		return "C";
		break;       
	case DIK_V    :
		return "V";
		break;       
	case DIK_B    :
		return "B";
		break;       
	case DIK_N     :
		return "N";
		break;      
	case DIK_M     :
		return "M";
		break;      
	case DIK_COMMA   :
		return "Comma";
		break;    
	case DIK_PERIOD  :
		return "Period";
		break;    
	case DIK_SLASH   :
		return "Slash";
		break;    
	case DIK_RSHIFT  :
		return "Right Shift";
		break;    
	case DIK_MULTIPLY :
		return "Multiply";
		break;   
	case DIK_LMENU    :
		return "Left Menu";
		break;   
	case DIK_SPACE    :
		return "Space";
		break;   
	case DIK_CAPITAL  :
		return "Capital";
		break;   
	case DIK_F1     :
		return "F1";
		break;     
	case DIK_F2      :
		return "F2";
		break;    
	case DIK_F3   :
		return "F3";
		break;       
	case DIK_F4    :
		return "F4";
		break;      
	case DIK_F5    :
		return "F5";
		break;      
	case DIK_F6     :
		return "F6";
		break;     
	case DIK_F7     :
		return "F7";
		break;     
	case DIK_F8     :
		return "F8";
		break;     
	case DIK_F9    :
		return "F9";
		break;      
	case DIK_F10   :
		return "F10";
		break;      
	case DIK_NUMLOCK :
		return "Num Lock";
		break;    
	case DIK_SCROLL  :
		return "Scroll";
		break;    
	case DIK_NUMPAD7  :
		return "Numpad 7";
		break;   
	case DIK_NUMPAD8  :
		return "Numpad 8";
		break;   
	case DIK_NUMPAD9  :
		return "Numpad 9";
		break;   
	case DIK_SUBTRACT :
		return "Subtract";
		break;   
	case DIK_NUMPAD4 :
		return "Numpad 4";
		break;    
	case DIK_NUMPAD5 :
		return "Numpad 5";
		break;    
	case DIK_NUMPAD6 :
		return "Numpad 6";
		break;    
	case DIK_ADD     :
		return "Add";
		break;    
	case DIK_NUMPAD1 :
		return "Numpad 1";
		break;    
	case DIK_NUMPAD2 :
		return "Numpad 2";
		break;    
	case DIK_NUMPAD3 :
		return "Numpad 3";
		break;    
	case DIK_NUMPAD0 :
		return "Numpad 0";
		break;    
	case DIK_DECIMAL :
		return "Decimal";
		break;    
	case DIK_OEM_102 :
		return "OEM 102";
		break;    
	case DIK_F11    :
		return "F11";
		break;     
	case DIK_F12    :
		return "F12";
		break;     
	case DIK_F13    :
		return "F13";
		break;     
	case DIK_F14     :
		return "F14";
		break;    
	case DIK_F15     :
		return "F15";
		break;    
	case DIK_KANA    :
		return "Kana";
		break;    
	case DIK_ABNT_C1  :
		return "ABNT C1";
		break;   
	case DIK_CONVERT  :
		return "Convert";
		break;   
	case DIK_NOCONVERT :
		return "No Convert";
		break;  
	case DIK_YEN       :
		return "Yen";
		break;  
	case DIK_ABNT_C2  :
		return "ABNT C2";
		break;   
	case DIK_NUMPADEQUALS:
		return "Numpad Equals";
		break;
	case DIK_PREVTRACK  :
		return "Prev Track";
		break; 
	case DIK_AT        :
		return "At";
		break;  
	case DIK_COLON  :
		return "Colon";
		break;     
	case DIK_UNDERLINE  :
		return "Underline";
		break; 
	case DIK_KANJI    :
		return "Kanji";
		break;   
	case DIK_STOP     :
		return "Stop";
		break;   
	case DIK_AX       :
		return "Ax";
		break;   
	case DIK_UNLABELED   :
		return "Unlabeled";
		break;
	case DIK_NEXTTRACK   :
		return "Next Track";
		break;
	case DIK_NUMPADENTER :
		return "Numpad Enter";
		break;
	case DIK_RCONTROL    :
		return "Right Control";
		break;
	case DIK_MUTE      :
		return "Mute";
		break;  
	case DIK_CALCULATOR  :
		return "Calculator";
		break;
	case DIK_PLAYPAUSE   :
		return "Play-Pause";
		break;
	case DIK_MEDIASTOP   :
		return "Media Stop";
		break;
	case DIK_VOLUMEDOWN  :
		return "Volume Down";
		break;
	case DIK_VOLUMEUP   :
		return "Volume Up";
		break; 
	case DIK_WEBHOME    :
		return "Web Home";
		break; 
	case DIK_NUMPADCOMMA :
		return "Numpad Comma";
		break;
	case DIK_DIVIDE  :
		return "Divide";
		break;    
	case DIK_SYSRQ  :
		return "Sys Rq";
		break;     
	case DIK_RMENU  :
		return "Right Menu";
		break;     
	case DIK_PAUSE :
		return "Pause";
		break;      
	case DIK_HOME  :
		return "Home";
		break;      
	case DIK_UP :
		return "Up";
		break;         
	case DIK_PRIOR   :
		return "Prior";
		break;    
	case DIK_LEFT   :
		return "Left";
		break;     
	case DIK_RIGHT   :
		return "Right";
		break;    
	case DIK_END     :
		return "End";
		break;    
	case DIK_DOWN    :
		return "Down";
		break;    
	case DIK_NEXT    :
		return "Next";
		break;    
	case DIK_INSERT   :
		return "Insert";
		break;   
	case DIK_DELETE  :
		return "Delete";
		break;
	case DIK_LWIN   :
		return "Left Windows";
		break;     
	case DIK_RWIN   :
		return "Right Windows";
		break;     
	case DIK_APPS   :
		return "Apps";
		break;     
	case DIK_POWER   :
		return "Power";
		break;    
	case DIK_SLEEP    :
		return "Sleep";
		break;   
	case DIK_WAKE     :
		return "Wake";
		break;   
	case DIK_WEBSEARCH   :
		return "Web Search";
		break;
	case DIK_WEBFAVORITES:
		return "Web Favorites";
		break;
	case DIK_WEBREFRESH  :
		return "Web Refresh";
		break;
	case DIK_WEBSTOP    :
		return "Web Stop";
		break; 
	case DIK_WEBFORWARD  :
		return "Web Forward";
		break;
	case DIK_WEBBACK    :
		return "Web Back";
		break; 
	case DIK_MYCOMPUTER  :
		return "My Computer";
		break;
	case DIK_MAIL        :
		return "Mail";
		break;
	case DIK_MEDIASELECT :
		return "Media Select";
		break;
	default:
		return"No Key";
		break;

	}
}


void CInputManager::UpdateInput()
{
	memcpy(m_OldKeyState, m_KeyState, 256);
	memcpy(&m_OldMouseState, &m_MouseState, sizeof(DIMOUSESTATE));

	m_pKeyboard->GetDeviceState(sizeof(m_Keys), (void*)m_Keys);
	memcpy(m_KeyState, m_Keys, 256);

	m_pMouse->GetDeviceState(sizeof(DIMOUSESTATE), &m_MouseState);
}


void CInputManager::SetMouseXPos(long nPosX)
{
	POINT cursor;
	cursor.y = MouseGetPosY();
	cursor.x = nPosX;
	ClientToScreen(m_hWnd, &cursor);
	SetCursorPos(cursor.x,cursor.y);
}


void CInputManager::SetMouseYPos(long nPosY)
{
	POINT cursor;
	cursor.y = nPosY;
	cursor.x = MouseGetPosX();
	ClientToScreen(m_hWnd, &cursor);
	SetCursorPos(cursor.x,cursor.y);
}